home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / smtpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-10  |  22.3 KB  |  975 lines

  1. /*
  2.  *    CLIENT routines for Simple Mail Transfer Protocol ala RFC821
  3.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *     Modified 14 June 1987 by P. Karn for symbolic target addresses,
  8.  *    also rebuilt locking mechanism
  9.  *    Copyright 1987 1988 David Trulli, All Rights Reserved.
  10.  *    Permission granted for non-commercial copying and use, provided
  11.  *    this notice is retained.
  12.  */
  13. #include <stdio.h>
  14. #include <fcntl.h>
  15. #include <time.h>
  16. #include <setjmp.h>
  17. #include <ctype.h>
  18. #ifdef UNIX
  19. #include <sys/types.h>
  20. #endif
  21. #ifdef    AMIGA
  22. #include "stat.h"
  23. #else
  24. #include <sys/stat.h>
  25. #endif
  26. #ifdef    __TURBOC__
  27. #include <dir.h>
  28. #include <io.h>
  29. #endif
  30. #include "global.h"
  31. #include "config.h"
  32. #ifdef    ANSIPROTO
  33. #include <stdarg.h>
  34. #endif
  35. #include "cmdparse.h"
  36. #include "proc.h"
  37. #include "socket.h"
  38. #ifdef LZW
  39. #include "lzw.h"
  40. #endif
  41. #include "timer.h"
  42. #include "netuser.h"
  43. #include "smtp.h"
  44. #include "dirutil.h"
  45. #include "commands.h"
  46. #include "session.h"
  47. #include "files.h"
  48.  
  49. int16 Smtpquiet = 0;
  50. int Smtpmode = 0;
  51. static int Smtpsessions = 0;        /* number of client connections */
  52. static struct timer Smtpcli_t;
  53. static int32 Gateway;
  54. static unsigned short Smtpmaxcli = MAXSESSIONS;    /* the max client connections allowed */
  55. static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  56.  
  57. #ifdef SMTPTRACE
  58. static int Smtptrace = 0;        /* used for trace level */
  59. static int dosmtptrace __ARGS((int argc,char *argv[],void *p));
  60. #endif
  61.  
  62. #ifdef LZW
  63. int Smtplzw = 1;
  64. static int Smtpbatch = 1;
  65. #else
  66. static int Smtpbatch = 0;
  67. #endif
  68.  
  69. static int near getresp __ARGS((struct smtpcli *ftp,int mincode));
  70. static void near logerr __ARGS((struct smtpcli *cb,char *line));
  71. static void smtp_send __ARGS((int unused,void *cb1,void *p));
  72. static void near del_job __ARGS((struct smtp_job *jp));
  73. static struct smtpcli * near lookup __ARGS((int32 destaddr));
  74. static struct smtpcli * near newcb __ARGS((void));
  75. static struct smtp_job * near setupjob __ARGS((struct smtpcli *cb,char *id,char *from));
  76. static void near del_session __ARGS((struct smtpcli *cb));
  77. static void near retmail __ARGS((struct smtpcli *cb));
  78. static int near next_job __ARGS((struct smtpcli *cb));
  79.  
  80. /* delete a list of list structs */
  81. void
  82. del_list(lp)
  83. struct list *lp;
  84. {
  85.     struct list *tp, *tp1 = NULLLIST;
  86.  
  87.     for (tp = lp; tp != NULLLIST; tp = tp1) {
  88.         tp1 = tp->next;
  89.         xfree(tp->val);
  90.         xfree((char *)tp);
  91.     }
  92. }
  93.  
  94. static void near
  95. del_job(jp)
  96. struct smtp_job *jp;
  97. {
  98.     if ( *jp->jobname != '\0')
  99.         rmlock(Mailqdir,jp->jobname);
  100.     xfree(jp->from);
  101.     del_list(jp->to);
  102.     xfree((char *)jp);
  103. }
  104.  
  105. /* free the message struct and data */
  106. static void near
  107. del_session(cb)
  108. struct smtpcli *cb;
  109. {
  110.     struct smtp_job *jp,*tp;
  111.     int i;
  112.  
  113.     if (cb == NULLSMTPCLI)
  114.         return;
  115.     for(i=0; i<MAXSESSIONS; i++)
  116.         if(cli_session[i] == cb) {
  117.             cli_session[i] = NULLSMTPCLI;
  118.             break;
  119.         }
  120.  
  121.     xfree(cb->wname);
  122.     xfree(cb->tname);
  123.     xfree(cb->destname);
  124.     for (jp = cb->jobq; jp != NULLJOB; jp = tp) {
  125.         tp = jp->next;
  126.         del_job(jp);
  127.     }
  128.     del_list(cb->errlog);
  129.     xfree((char *)cb);
  130.     Smtpsessions--;    /* number of connections active */
  131. }
  132.  
  133. /* called to advance to the next job */
  134. static int near
  135. next_job(cb)
  136. struct smtpcli *cb;
  137. {
  138.     struct smtp_job *jp = cb->jobq->next;
  139.  
  140.     del_job(cb->jobq);
  141.     /* remove the error log of previous message */
  142.     del_list(cb->errlog);
  143.     cb->errlog = NULLLIST;
  144.     cb->jobq = jp;
  145.     if (jp == NULLJOB)
  146.         return 0;
  147.     sprintf(cb->tname,"%s/%s.txt",Mailqdir,jp->jobname);
  148.     sprintf(cb->wname,"%s/%s.wrk",Mailqdir,jp->jobname);
  149. #ifdef SMTPTRACE
  150.     if (Smtptrace) {
  151.         tprintf("sending job %s\n",jp->jobname);
  152.     }
  153. #endif
  154.     return 1;
  155. }
  156.  
  157. /* create a new  smtp control block */
  158. static struct smtpcli * near
  159. newcb()
  160. {
  161.     int i;
  162.     struct smtpcli *cb;
  163.  
  164.     for(i=0; i<MAXSESSIONS; i++) {
  165.         if(cli_session[i] == NULLSMTPCLI) {
  166.             cb = (struct smtpcli *)mxallocw(sizeof(struct smtpcli));
  167.             cb->wname = mxallocw((unsigned)strlen(Mailqdir)+JOBNAME);
  168.             cb->tname = mxallocw((unsigned)strlen(Mailqdir)+JOBNAME);
  169.             cli_session[i] = cb;
  170.             Smtpsessions++;    /* number of connections active */
  171.             return(cb);
  172.         }
  173.     }
  174.     return NULLSMTPCLI;
  175. }
  176.  
  177. /* look to see if a smtp control block exists for this ipdest */
  178. static struct smtpcli * near
  179. lookup(destaddr)
  180. int32 destaddr;
  181. {
  182.     int i;
  183.  
  184.     for(i = 0; i < MAXSESSIONS; i++) {
  185.         if (cli_session[i] == NULLSMTPCLI)
  186.             continue;
  187.         if(cli_session[i]->ipdest == destaddr)
  188.             return cli_session[i];
  189.     }
  190.     return NULLSMTPCLI;
  191. }
  192.  
  193. /* add this job to control block queue */
  194. static struct smtp_job * near
  195. setupjob(cb,id,from)
  196. struct smtpcli *cb;
  197. char *id,*from;
  198. {
  199.     struct smtp_job *p2, *p1 =
  200.         (struct smtp_job *)mxallocw(sizeof(struct smtp_job));
  201.  
  202.     p1->from = strxdup(from);
  203.     strcpy(p1->jobname,id);
  204.     /* now add to end of jobq */
  205.     if ((p2 = cb->jobq) == NULLJOB)
  206.         cb->jobq = p1;
  207.     else {
  208.         while(p2->next != NULLJOB)
  209.             p2 = p2->next;
  210.         p2->next = p1;
  211.     }
  212.     return p1;
  213. }
  214.  
  215. /* This is the routine that gets called every so often to do outgoing
  216.  * mail processing. When called with a null argument, it runs the entire
  217.  * queue; if called with a specific non-zero IP address from the remote
  218.  * kick server, it only starts up sessions to that address.
  219.  */
  220. int
  221. smtptick(t)
  222. void *t;
  223. {
  224.     struct smtpcli *cb;
  225.     struct smtp_job *jp;
  226.     struct list *ap;
  227.     char    tmpstring[LINELEN], wfilename[13], prefix[9];
  228.     char    from[LINELEN], to[LINELEN], *cp, *cp1;
  229.     int32 destaddr,target;
  230.     FILE *wfile;
  231.     int i;
  232.  
  233.     stop_timer(&Smtpcli_t);
  234.     target = (int32)t;
  235. #ifdef SMTPTRACE
  236.     if (Smtptrace)
  237.         tprintf("smtp daemon entered, target = %s\n",inet_ntoa(target));
  238. #endif
  239.     if(availmem() < Memthresh){
  240.         /* Memory is tight, don't do anything */
  241.         /* Restart timer */
  242.         start_timer(&Smtpcli_t);
  243.         return 0;
  244.     }
  245.     for(filedir(Mailqueue,0,wfilename);wfilename[0] != '\0';
  246.       filedir(Mailqueue,1,wfilename)){
  247.         pwait(NULL);
  248.         /* save the prefix of the file name which it job id */
  249.         cp = wfilename;
  250.         cp1 = prefix;
  251.         while (*cp && *cp != '.')
  252.             *cp1++ = *cp++;
  253.         *cp1 = '\0';
  254.  
  255.         /* lock this file from the smtp daemon */
  256.         if (mlock(Mailqdir,prefix))
  257.             continue;
  258.  
  259.         sprintf(tmpstring,"%s/%s",Mailqdir,wfilename);
  260.         if ((wfile = fopen(tmpstring,READ_TEXT)) == NULLFILE) {
  261.             /* probably too many open files */
  262.             rmlock(Mailqdir,prefix);
  263.             /* continue to next message. The failure
  264.             * may be temporary */
  265.             continue;
  266.         }
  267.  
  268.         fgets(tmpstring,sizeof(tmpstring),wfile);    /* read target host */
  269.         rip(tmpstring);
  270.  
  271.         if ((destaddr = mailroute(tmpstring)) == 0) {
  272.             fclose(wfile);
  273.             tprintf("*** smtp: Unknown address %s\n",tmpstring);
  274.             rmlock(Mailqdir,prefix);
  275.             continue;
  276.         }
  277.         if(target != 0 && destaddr != target){
  278.             fclose(wfile);
  279.             rmlock(Mailqdir,prefix);
  280.             continue;    /* Not the proper target of a kick */
  281.         }
  282. #ifdef EVENT
  283.         if (!testdelv(destaddr)) {
  284.             fclose(wfile)
  285.             rmlock(Mailqdir,prefix);
  286.             continue;
  287.         }
  288. #endif
  289.         if ((cb = lookup(destaddr)) == NULLSMTPCLI) {
  290.             /* there are enough processes running already */
  291.             if (Smtpsessions >= Smtpmaxcli) {
  292. #ifdef SMTPTRACE
  293.                 if (Smtptrace) {
  294.                     tputs("smtp daemon: too many processes\n");
  295.                 }
  296. #endif
  297.                 fclose(wfile);
  298.                 rmlock(Mailqdir,prefix);
  299.                 break;
  300.             }
  301.             if ((cb = newcb()) == NULLSMTPCLI) {
  302.                 fclose(wfile);
  303.                 rmlock(Mailqdir,prefix);
  304.                 break;
  305.             }
  306.             cb->ipdest = destaddr;
  307.             cb->destname = strxdup(tmpstring);
  308.         } else {
  309.             if(cb->lock){
  310.                 /* This system is already is sending mail lets not
  311.                 * interfere with its send queue.
  312.                 */
  313.                 fclose(wfile);
  314.                 rmlock(Mailqdir,prefix);
  315.                 continue;
  316.             }
  317.         }
  318.  
  319.         fgets(from,sizeof(from),wfile);    /* read from */
  320.         rip(from);
  321.         if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
  322.             fclose(wfile);
  323.             rmlock(Mailqdir,prefix);
  324.             del_session(cb);
  325.             break;
  326.         }
  327.         while (fgets(to,sizeof(to),wfile) != NULLCHAR) {
  328.             rip(to);
  329.             if (addlist(&jp->to,to,DOMAIN) == NULLLIST) {
  330.                 fclose(wfile);
  331.                 del_session(cb);
  332.             }
  333.         }
  334.         fclose(wfile);
  335. #ifdef SMTPTRACE
  336.         if (Smtptrace) {
  337.             tprintf("queue job %s From: %s To:",prefix,from);
  338.             for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  339.                 tprintf(" %s",ap->val);
  340.             tputs("\n");
  341.         }
  342. #endif
  343.     }
  344.  
  345.     /* start sending that mail */
  346.     for(i = 0; i < MAXSESSIONS; i++) {
  347.         if((cb = cli_session[i]) == NULLSMTPCLI)
  348.             continue;
  349.         if(cb->lock)
  350.             continue;
  351.         sprintf(cb->tname,"%s/%s.txt",Mailqdir,cb->jobq->jobname);
  352.         sprintf(cb->wname,"%s/%s.wrk",Mailqdir,cb->jobq->jobname);
  353.         newproc("smtp_send", 2048, smtp_send, 0, cb, NULL, 0);
  354.  
  355. #ifdef SMTPTRACE
  356.         if (Smtptrace)
  357.             tprintf("Trying Connection to %s\n",inet_ntoa(cb->ipdest));
  358. #endif
  359.     }
  360.     /* Restart timer */
  361.     start_timer(&Smtpcli_t);
  362.     return 0;
  363. }
  364.  
  365. /* stub for calling mdaemon to return message to sender */
  366. static void near
  367. retmail(cb)
  368. struct smtpcli *cb;
  369. {
  370.     FILE *infile;
  371. #ifdef SMTPTRACE
  372.     if (Smtptrace) {
  373.         tprintf("smtp job %s returned to sender\n",cb->wname);
  374.     }
  375. #endif
  376.     if ((infile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
  377.         return;
  378.     mdaemon(infile,cb->jobq->from,cb->errlog,1);
  379.     fclose(infile);
  380. }
  381.  
  382. /* This is the master state machine that handles a single SMTP transaction.
  383.  * It is called with a queue of jobs for a particular host.
  384.  * The logic is complicated by the "Smtpbatch" variable, which controls
  385.  * the batching of SMTP commands. If Smtpbatch is true, then many of the
  386.  * SMTP commands are sent in one swell foop before waiting for any of
  387.  * the responses. Unfortunately, this breaks many brain-damaged SMTP servers
  388.  * out there, so provisions have to be made to operate SMTP in lock-step mode.
  389.  */
  390. static void
  391. smtp_send(unused,cb1,p)
  392. int unused;
  393. void *cb1;
  394. void *p;
  395. {
  396.     struct smtpcli *cb = (struct smtpcli *)cb1;
  397.     struct list *tp;
  398.     struct sockaddr_in fsocket;
  399.     char *cp1;
  400.     int smtpbatch, rcode, rcpts, goodrcpt, i, init = 1;
  401. #ifdef LZW
  402.     int lzwmode, lzwbits;
  403.     extern int16 Lzwbits;
  404.     extern int Lzwmode;
  405. #endif
  406.  
  407.     cb->lock = 1;
  408.     fsocket.sin_family = AF_INET;
  409.     fsocket.sin_addr.s_addr = cb->ipdest;
  410.     fsocket.sin_port = IPPORT_SMTP;
  411.  
  412.     cb->s = socket(AF_INET,SOCK_STREAM,0);
  413.     sockmode(cb->s,SOCK_ASCII);
  414.     setflush(cb->s,-1);    /* We'll explicitly flush before reading */
  415.  
  416. #ifdef SMTPTRACE
  417.     if (Smtptrace)
  418.         tputs("SMTP client Trying...\n");
  419. #endif
  420.     if(connect(cb->s,(char *)&fsocket,SOCKSIZE) == 0){
  421. #ifdef SMTPTRACE
  422.         if (Smtptrace)
  423.             tputs("Connected\n");
  424. #endif
  425.         ;
  426.     } else {
  427.         cp1 = sockerr(cb->s);
  428. #ifdef SMTPTRACE
  429.         if (Smtptrace)
  430.             tprintf("Connect failed: %s\n",cp1 != NULLCHAR ? cp1 : "");
  431. #endif
  432.         log(cb->s,"SMTP %s Connect failed: %s",psocket(&fsocket),
  433.             cp1 != NULLCHAR ? cp1 : "");
  434.     }
  435. #ifdef LZW
  436.     rcode = getresp(cb,200);
  437.     if(rcode == -1 || rcode >= 400)
  438.         goto quit;
  439.  
  440.     if(Smtplzw && cb->ipdest != Ip_addr) {
  441.         char cp[LINELEN], *cp1, *cp2;
  442.         usprintf(cb->s,"XLZW %d %d\n",Lzwbits,Lzwmode);
  443.         usflush(cb->s);
  444.  
  445.         if(recvline(cb->s,cp,LINELEN) == -1)
  446.             goto quit;
  447.         rip(cp);
  448.         if((cp1 = strchr(cp,' ')) == NULLCHAR)
  449.             goto quit;
  450.         *cp1 = '\0';
  451.  
  452.         smtpbatch = 1;
  453.         if((rcode = atoi(cp)) == 252) {
  454.             lzwinit(cb->s,Lzwbits,Lzwmode);
  455.         } else if(rcode == 250) {
  456.             lzwbits = LZWBITS;
  457.             lzwmode = LZWMODE;
  458.             cp1++;
  459.             if((cp2 = strchr(cp1,' ')) != NULLCHAR) {
  460.                 *cp2 = '\0';
  461.                 lzwmode = atoi(cp1);
  462.                 cp1++;
  463.                 if((cp2 = strchr(cp1,' ')) != NULLCHAR) {
  464.                     *cp2 = '\0';
  465.                     lzwbits = atoi(cp1);
  466.                 } else {
  467.                     lzwmode = LZWMODE;
  468.                 }
  469.             }
  470.             lzwinit(cb->s,lzwbits,lzwmode);
  471.         } else {
  472.             smtpbatch = Smtpbatch;
  473.         }
  474.     } else {
  475.         smtpbatch = Smtpbatch;
  476.     }
  477. #else
  478.     smtpbatch = Smtpbatch;
  479.     if(!smtpbatch) {
  480.         rcode = getresp(cb,200);
  481.         if(rcode == -1 || rcode >= 400)
  482.             goto quit;
  483.     }
  484. #endif
  485.  
  486.     /* Say HELO */
  487.     usprintf(cb->s,"HELO %s\n",Hostname);
  488.     if(!smtpbatch){
  489.         rcode = getresp(cb,200);
  490.         if(rcode == -1 || rcode >= 400)
  491.             goto quit;
  492.     }
  493.     do {    /* For each message... */
  494.  
  495.         /* if this file open fails, skip it */
  496.         if ((cb->tfile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
  497.             continue;
  498.  
  499.         /* Send MAIL and RCPT commands */
  500.         usprintf(cb->s,"MAIL FROM:<%s>\n",cb->jobq->from);
  501.         if(!smtpbatch){
  502.             rcode = getresp(cb,200);
  503.             if(rcode == -1 || rcode >= 400)
  504.                 goto quit;
  505.         }
  506.         rcpts = 0;
  507.         goodrcpt = 0;
  508.         for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
  509.             usprintf(cb->s,"RCPT TO:<%s>\n",tp->val);
  510.             if(!smtpbatch){
  511.                 rcode = getresp(cb,200);
  512.                 if(rcode == -1)
  513.                     goto quit;
  514.                 if(rcode < 400)
  515.                     goodrcpt = 1; /* At least one good */
  516.             }
  517.             rcpts++;
  518.         }
  519.         /* Send DATA command */
  520.         usputs(cb->s,"DATA\n");
  521.         if(!smtpbatch){
  522.             rcode = getresp(cb,200);
  523.             if(rcode == -1 || rcode >= 400)
  524.                 goto quit;
  525.         }
  526.         if(smtpbatch){
  527.             /* Now wait for the responses to come back. The first time
  528.              * we do this, we wait first for the start banner and
  529.              * HELO response. In any case, we wait for the response to
  530.              * the MAIL command here.
  531.              */
  532. #ifdef LZW
  533.             for(i = init ? 2 : 1; i > 0; i--) {
  534. #else
  535.             for(i = init ? 3 : 1; i > 0; i--) {
  536. #endif
  537.                 rcode = getresp(cb,200);
  538.                 if(rcode == -1 || rcode >= 400)
  539.                     goto quit;
  540.             }
  541.             init = 0;
  542.  
  543.             /* Now process the responses to the RCPT commands */
  544.             for(i=rcpts;i!=0;i--){
  545.                 rcode = getresp(cb,200);
  546.                 if(rcode == -1)
  547.                     goto quit;
  548.                 if(rcode < 400)
  549.                     goodrcpt = 1; /* At least one good */
  550.             }
  551.             /* And finally get the response to the DATA command.
  552.              * Some servers will return failure here if no recipients
  553.              * are valid, some won't.
  554.              */
  555.             rcode = getresp(cb,200);
  556.             if(rcode == -1 || rcode >= 400)
  557.                 goto quit;
  558.  
  559.             /* check for no good rcpt on the list */
  560.             if (goodrcpt == 0){
  561.                 usputs(cb->s,".\n");  /* Get out of data mode */
  562.                 goto quit;
  563.             }
  564.         }
  565.         /* Send the file. This also closes it */
  566.         strcpy(cb->buf,"\n");
  567.         while(fgets(cb->buf,sizeof(cb->buf),cb->tfile) != NULLCHAR) {
  568.             /* Escape a single '.' character at the beginning of a line */
  569.             if(strcmp(cb->buf,".\n") == 0)
  570.                 usputc(cb->s,'.');
  571.             usputs(cb->s,cb->buf);
  572.         }
  573.         fclose(cb->tfile);
  574.         cb->tfile = NULLFILE;
  575.         /* Send the end-of-message command */
  576.         usprintf(cb->s,"%s.\n",(cb->buf[strlen(cb->buf)-1] == '\n') ? "" : "\n");
  577.  
  578.         /* Wait for the OK response */
  579.         rcode = getresp(cb,200);
  580.         if(rcode == -1)
  581.             goto quit;
  582.         if((rcode >= 200 && rcode < 300) || rcode >= 500){
  583.             /* if a good transfer or permanent failure remove job */
  584.  
  585.             if (cb->errlog != NULLLIST)
  586.                 retmail(cb);
  587.             /* Unlink the textfile */
  588.             unlink(cb->tname);
  589.             unlink(cb->wname);    /* remove workfile */
  590.             log(cb->s,"SMTP sent job %s To: %s From: %s",
  591.              cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  592.         }
  593.     } while(next_job(cb));
  594. quit:
  595.     usputs(cb->s,"QUIT\n");
  596.     if (cb->errlog != NULLLIST){
  597.         retmail(cb);
  598.         unlink(cb->wname);    /* unlink workfile */
  599.         unlink(cb->tname);    /* unlink text */
  600.     }
  601.     close_s(cb->s);
  602.     if(cb->tfile != NULLFILE)
  603.         fclose(cb->tfile);
  604.     cb->lock = 0;
  605.     del_session(cb);
  606. }
  607.  
  608.  
  609. /* create mail lockfile */
  610. int
  611. mlock(dir,id)
  612. char *dir,*id;
  613. {
  614.     int fd;
  615.     char lockname[LINELEN];
  616.  
  617. #ifdef    MSDOS
  618.     if(strlen(id) > 8) {        /* truncate long filenames */
  619.         id[8] = '\0';
  620.         if(id[7] == '/')
  621.             id[7] = '\0';
  622.     }
  623. #endif
  624.     /* Try to create the lock file in an atomic operation */
  625.     sprintf(lockname,"%s/%s.lck",dir,id);
  626. #ifdef        AMIGA
  627.     /* don't ask, really, just don't ask... I'd do file locking on
  628.      * an Amiga much more differently than this.
  629.      */
  630.     if(access(lockname, 0) == 0)
  631.         return -1;
  632. #endif
  633.     fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600);
  634.     if(fd != -1)
  635.         close(fd);
  636.     return (fd == -1) ? -1 : 0;
  637. }
  638.  
  639. /* remove mail lockfile */
  640. int
  641. rmlock(dir,id)
  642. char *dir,*id;
  643. {
  644.     char lockname[LINELEN];
  645.  
  646. #ifdef    MSDOS
  647.     if(strlen(id) > 8) {        /* truncate long filenames */
  648.         id[8] = '\0';
  649.         if(id[7] == '/')
  650.             id[7] = '\0';
  651.     }
  652. #endif
  653.     sprintf(lockname,"%s/%s.lck",dir,id);
  654.     return(unlink(lockname));
  655. }
  656.  
  657. /* Mail routing function. For now just use the hosts file */
  658. int32
  659. mailroute(dest)
  660. char *dest;
  661. {
  662.     int32 destaddr = 0;
  663.  
  664.     /* look up address or use the gateway */
  665.     destaddr = resolve(dest);
  666.     return ((destaddr != 0) ? destaddr : (Gateway != 0) ? Gateway : 0);
  667. }
  668.  
  669. /* save line in error list */
  670. static void near
  671. logerr(cb,line)
  672. struct smtpcli *cb;
  673. char *line;
  674. {
  675.     struct list *lp,*tp;
  676.  
  677.     tp = (struct list *)mxallocw(sizeof(struct list));
  678.     tp->val = strxdup(line);
  679.  
  680.     /* find end of list */
  681.     if ((lp = cb->errlog) == NULLLIST)
  682.         cb->errlog = tp;
  683.     else {
  684.         while(lp->next != NULLLIST)
  685.             lp = lp->next;
  686.         lp->next = tp;
  687.     }
  688. }
  689.  
  690. /* Wait for, read and display response from server. Return the result code. */
  691. static int near
  692. getresp(cb,mincode)
  693. struct smtpcli *cb;
  694. int mincode;    /* Keep reading until at least this code comes back */
  695. {
  696.     int rval;
  697.     char line[LINELEN];
  698.  
  699.     usflush(cb->s);
  700.  
  701.     for(;;){
  702.         /* Get line */
  703.         if(recvline(cb->s,line,sizeof(line)) == -1){
  704.             rval = -1;
  705.             break;
  706.         }
  707.         rip(line);        /* Remove cr/lf */
  708.         rval = atoi(line);
  709. #ifdef    SMTPTRACE
  710.         if(Smtptrace)
  711.             tprintf("smtp recv: %s\n",line);/* Display to user */
  712. #endif
  713.         if(rval >= 500) {    /* Save permanent error replies */
  714.             char tmp[LINELEN];
  715.             if(cb->errlog == NULLLIST) {
  716.                 sprintf(tmp,"While talking to %s:",cb->destname);
  717.                 logerr(cb,tmp);
  718.             }
  719.             if(cb->buf[0] != '\0') {    /* Save offending command */
  720.                 rip(cb->buf);
  721.                 sprintf(tmp,">>> %s",cb->buf);
  722.                 logerr(cb,tmp);
  723.                 cb->buf[0] = '\0';
  724.             }
  725.             sprintf(tmp,"<<< %s",line);
  726.             logerr(cb,tmp);        /* save the error reply */
  727.         }
  728.         /* Messages with dashes are continued */
  729.         if(line[3] != '-' && rval >= mincode)
  730.             break;
  731.     }
  732.     return rval;
  733. }
  734.  
  735. /* ----------------------- SMTP Client subcmds ----------------------- */
  736.  
  737. static int
  738. dobatch(argc,argv,p)
  739. int argc;
  740. char *argv[];
  741. void *p;
  742. {
  743.     return setbool(&Smtpbatch,"SMTP batching",argc,argv);
  744. }
  745.  
  746. static int
  747. dogateway(argc,argv,p)
  748. int argc;
  749. char *argv[];
  750. void *p;
  751. {
  752.     int32 n;
  753.  
  754.     if(argc < 2){
  755.         tprintf("SMTP gateway: %s\n",inet_ntoa(Gateway));
  756.     } else if((n = resolve(argv[1])) == 0){
  757.         tprintf(Badhost,argv[1]);
  758.         return 1;
  759.     } else
  760.         Gateway = n;
  761.     return 0;
  762. }
  763.  
  764. static int
  765. smtpkick(argc,argv,p)
  766. int argc;
  767. char *argv[];
  768. void *p;
  769. {
  770.     int32 addr = 0;
  771.     if(argc > 1 && (addr = resolve(argv[1])) == 0){
  772.         tprintf(Badhost,argv[1]);
  773.         return 1;
  774.     }
  775.     smtptick((void *)addr);
  776.     return 0;
  777. }
  778.  
  779. /* kill a job in the mqueue */
  780. static int
  781. dosmtpkill(argc,argv,p)
  782. int argc;
  783. char *argv[];
  784. void *p;
  785. {
  786.     char s[SLINELEN], *cp, c;
  787.  
  788.     sprintf(s,"%s/%s.lck",Mailqdir,argv[1]);
  789.     cp = strrchr(s,'.');
  790.     if (!access(s,0)) {
  791.         Current->ttystate.echo = Current->ttystate.edit = 0;
  792.         c = keywait("Warning, the job is locked by SMTP. Remove (y/n)? ",0);
  793.         Current->ttystate.echo = Current->ttystate.edit = 1;
  794.         if (c != 'y') {
  795.             return 0;
  796.         }
  797.         unlink(s);
  798.     }
  799.     strcpy(cp,".wrk");
  800.     if (unlink(s)) {
  801.         tprintf("Job id %s not found\n",argv[1]);
  802.     } else {
  803.         strcpy(cp,".txt");
  804.         unlink(s);
  805.     }
  806.     return 0;
  807. }
  808.  
  809. /* list jobs wating to be sent in the mqueue */
  810. static int
  811. dosmtplist(argc,argv,p)
  812. int argc;
  813. char *argv[];
  814. void *p;
  815. {
  816.     char tstring[80], line[20], host[LINELEN], from[LINELEN], to[LINELEN];
  817.     char status, *cp;
  818.     struct stat stbuf;
  819.     struct tm *tminfo, *localtime();
  820.     FILE *fp;
  821.  
  822.     Current->flowmode = Cooked;             /* Enable the more mechanism */
  823.     tputs("S     Job    Size Date  Time  Host             From\n");
  824.  
  825.     filedir(Mailqueue,0,line);
  826.     while(line[0] != '\0') {
  827.         sprintf(tstring,"%s/%s",Mailqdir,line);
  828.         if ((fp = open_file(tstring,READ_TEXT,0,1)) == NULLFILE)
  829.             continue;
  830.         if ((cp = strrchr(line,'.')) != NULLCHAR)
  831.             *cp = '\0';
  832.         sprintf(tstring,"%s/%s.lck",Mailqdir,line);
  833.         status = (access(tstring,0)) ? ' ' : 'L';
  834.         sprintf(tstring,"%s/%s.txt",Mailqdir,line);
  835.         stat(tstring,&stbuf);
  836.         tminfo = localtime(&stbuf.st_ctime);
  837.         fgets(host,sizeof(host),fp);
  838.         rip(host);
  839.         fgets(from,sizeof(from),fp);
  840.         rip(from);
  841.         tprintf("%c %7s %7ld %02d/%02d %02d:%02d %-16.16s %s\n",
  842.             status,
  843.             line,
  844.             stbuf.st_size,
  845.             tminfo->tm_mon+1,
  846.             tminfo->tm_mday,
  847.             tminfo->tm_hour,
  848.             tminfo->tm_min,
  849.             host,
  850.             from);
  851.         while (fgets(to,sizeof(to),fp) != NULLCHAR) {
  852.             rip(to);
  853.             tprintf("   To: %s\n",to);
  854.         }
  855.         fclose(fp);
  856.         filedir(Mailqueue,1,line);
  857.     }
  858.     Current->flowmode = Raw;
  859.     return 0;
  860. }
  861.  
  862. static int
  863. dosmtpmaxcli(argc,argv,p)
  864. int argc;
  865. char *argv[];
  866. void *p;
  867. {
  868.     return setshort(&Smtpmaxcli,"SMTP maxcli",argc,argv);
  869. }
  870.  
  871. static int
  872. setsmtpmode(argc,argv,p)
  873. int argc;
  874. char *argv[];
  875. void *p;
  876. {
  877.     if (argc < 2) {
  878.         tprintf("SMTP mode: %s\n",(Smtpmode & QUEUE) ? "queue" : "route");
  879.     } else {
  880.         switch(tolower(*argv[1])) {
  881.         case 'q':
  882.             Smtpmode |= QUEUE;
  883.             break;
  884.         case 'r':
  885.             Smtpmode &= ~QUEUE;
  886.             break;
  887.         default:
  888.             tputs("Usage: smtp mode [queue|route]\n");
  889.             break;
  890.         }
  891.     }
  892.     return 0;
  893. }
  894.  
  895. static int
  896. doquiet(argc,argv,p)
  897. int argc;
  898. char *argv[];
  899. void *p;
  900. {
  901.     return setintrc(&Smtpquiet,"SMTP quiet",argc,argv,0,3);
  902. }
  903.  
  904. #ifdef LZW
  905. static int
  906. dosmtplzw(argc,argv,p)
  907. int argc;
  908. char *argv[];
  909. void *p;
  910. {
  911.     return setbool(&Smtplzw,"SMTP lzw",argc,argv);
  912. }
  913. #endif
  914.  
  915. /* Set outbound spool scan interval */
  916. static int
  917. dotimer(argc,argv,p)
  918. int argc;
  919. char *argv[];
  920. void *p;
  921. {
  922.     if(argc < 2){
  923.         tprintf("SMTP timer %lu/%lu s\n",
  924.             read_timer(&Smtpcli_t)/1000L,
  925.             dur_timer(&Smtpcli_t)/1000L);
  926.         return 0;
  927.     }
  928.     stop_timer(&Smtpcli_t);
  929.     /* what to call on timeout */
  930.     Smtpcli_t.func = (void (*) __ARGS((void *)))smtptick;
  931.     Smtpcli_t.arg = NULLCHAR;                        /* dummy value */
  932.     set_timer(&Smtpcli_t,atol(argv[1]) * 1000L);    /* set timer duration */
  933.     start_timer(&Smtpcli_t);                        /* and fire it up */
  934.     return 0;
  935. }
  936.  
  937. #ifdef SMTPTRACE
  938. static int
  939. dosmtptrace(argc,argv,p)
  940. int argc;
  941. char *argv[];
  942. void *p;
  943. {
  944.     return setbool(&Smtptrace,"SMTP tracing",argc,argv);
  945. }
  946. #endif
  947.  
  948. int
  949. dosmtp(argc,argv,p)
  950. int argc;
  951. char *argv[];
  952. void *p;
  953. {
  954.     struct cmds Smtpcmds[] = {
  955.         "batch",        dobatch,        0,    0,    NULLCHAR,
  956.         "delete",        dosmtpkill,        0,    2,    "smtp delete <jobnumber>",
  957.         "gateway",        dogateway,        0,    0,    NULLCHAR,
  958.         "kick",            smtpkick,        0,    0,    NULLCHAR,
  959.         "list",            dosmtplist,        0,    0,    NULLCHAR,
  960. #ifdef LZW
  961.         "lzw",            dosmtplzw,        0,  0,  NULLCHAR,
  962. #endif
  963.         "maxclient",    dosmtpmaxcli,    0,    0,    NULLCHAR,
  964.         "mode",            setsmtpmode,    0,    0,    NULLCHAR,
  965.         "quiet",        doquiet,        0,    0,    NULLCHAR,
  966.         "timer",        dotimer,        0,    0,    NULLCHAR,
  967. #ifdef SMTPTRACE
  968.         "trace",        dosmtptrace,    0,     0,    NULLCHAR,
  969. #endif
  970.         NULLCHAR,
  971.     };
  972.     return subcmd(Smtpcmds,argc,argv,p);
  973. }
  974.  
  975.